import { ResultData } from '@/common/dto/result';
import { ErrorCode } from '@/enum/app.code';
import { HttpException, Injectable } from '@nestjs/common';
import { getManager, Repository } from 'typeorm';
import { UserEntity } from './users.entity';
import { compare, genSalt, hash } from 'bcryptjs';
import { CreateTokenDto } from '@/common/dto/CreateTokenDto';
import { JwtService } from '@nestjs/jwt';
import { ConfigService } from '@nestjs/config';
import { InjectRepository } from '@nestjs/typeorm';
import { UserDto, UserListDto } from './dto/UserDto';
import { instanceToPlain, plainToInstance } from 'class-transformer';
import chalk from 'chalk';
import { UserType } from '@/enum/common.enum';
import { AvatarGenerator } from '@/utils/randomAvator';

@Injectable()
export class UsersService {
  constructor(
    @InjectRepository(UserEntity)
    private userRepo: Repository<UserEntity>,
    private jwt: JwtService,
    private config: ConfigService
  ) {
    // 初始化管理员
    const username = this.config.get('user.username');
    const password = this.config.get('user.initialPassword');
    this.createSuperAdmin({ username, password } as UserDto)
      .then(() => {
        console.log(chalk.green(`初始化管理员成功，初始用户名:${username}，密码:${password}`));
      })
      .catch(() => {
        console.log(chalk.bgGrey(`已初始化管理员，初始用户名:${username}，密码:${password}`));
      });
  }
  /**
   * 创建管理员
   * @param user 用户信息
   * @returns
   */
  async createSuperAdmin(user: UserDto): Promise<ResultData> {
    const { username, password, type = UserType.SUPER_ADMIN } = user;

    if (!username || !password) {
      throw new HttpException('请输入用户名和密码', ErrorCode.PARAM_INVALID);
    }

    const existUser = await this.userRepo.findOne({ username });

    if (existUser) {
      throw new HttpException('用户已存在', ErrorCode.USER_CREATE_EXISTING);
    }
    // config.rules['require-atomic-updates'] = 0; 在eslint中关闭了atomic-updates的验证
    // const salt = await genSalt();
    // user.password = await hash(user.password, salt);
    user.password = await this.encryptPassword(user.password);
    user.avatar = new AvatarGenerator().generateRandomAvatar();
    const plainUser = plainToInstance(UserEntity, { type, ...user }, { ignoreDecorators: true });
    // getManager作用是获取实体管理器 .transaction 是事务 transcationalEntityManager  是实体管理器
    const result = await getManager().transaction((transcationalEntityManager) => {
      // transcationalEntityManager.save 是保存实体
      // transcationalEntityManager.restore 是恢复实体
      // transcationalEntityManager.remove 是删除实体
      // transcationalEntityManager.clear 是清空实体
      return transcationalEntityManager.save(plainUser);
    });
    return ResultData.ok(result);
  }
  /**
   * 加密密码
   * @param password 密码
   * @returns 加密后的密码
   */
  async encryptPassword(password: string): Promise<string> {
    return hash(password, await genSalt());
  }
  /**
   * 登录
   * @param username 用户名
   * @param password 密码
   * @returns 登录结果
   */
  public async login(username: string, password: string): Promise<ResultData> {
    // 判断用户是否存在
    // 不存在 error
    // 存在 生成token 登录成功
    let user = null;
    user = await this.userRepo.findOne({ username });
    if (!user) {
      return ResultData.error(ErrorCode.USER_NOT_FOUND, '用户不存在');
    }
    if (!(await compare(password, user.password))) {
      return ResultData.error(ErrorCode.PASSWORD_ERROR, '密码错误');
    }
    const data = this.getToken({ id: user.id });
    return ResultData.ok(data, '登录成功');
  }
  /**
   * 获取token
   * @param payload
   * @returns
   */
  public getToken(payload: { id: string }): CreateTokenDto {
    const accessToken = `Bearer ${this.jwt.sign(payload)}`;
    const refreshToken = `${this.jwt.sign(payload, {
      expiresIn: this.config.get('jwt.refreshTokenExpiresIn'),
    })}`;
    return { accessToken, refreshToken };
  }
  async userList(user: UserListDto): Promise<ResultData> {
    // 现在没有解决的问题
    // 1. 排序问题
    // 2. 删除密码属性 √
    // 3. 各种模糊查询
    const { page = 0, size = 10 } = user;
    Object.keys(user).forEach((key) => {
      if (user[key] === '') {
        delete user[key];
      }
    });

    const where = {
      // 只查询普通用户
      type: 2,
      ...user,
    };
    const [list, total] = await this.userRepo.findAndCount({
      where,
      // skip 是跳过多少条
      skip: page * size,
      // take 是取多少条
      take: size,
      order: {
        // Desc 是倒序 Asc 是正序
        id: 'DESC',
      },
    });
    return ResultData.ok({ list: instanceToPlain(list), total });
  }

  /**
   * 创建员工
   * @param user 员工信息
   * @returns
   */
  async create(user: UserDto): Promise<ResultData> {
    // 判断用户是否存在 有重复信息 则失败
    const { username } = user;
    if (await this.userRepo.findOne({ username })) {
      return ResultData.error(ErrorCode.USER_CREATE_EXISTING, '用户名已存在');
    }
    if (await this.userRepo.findOne({ phoneNum: user.phoneNum })) {
      return ResultData.error(ErrorCode.USER_CREATE_EXISTING, '当前手机号已存在，请调整后重新创建');
    }
    if (await this.userRepo.findOne({ email: user.email })) {
      return ResultData.error(ErrorCode.USER_CREATE_EXISTING, '当前邮箱已存在，请调整后重新注册');
    }
    user.password = await this.encryptPassword(user.password);
    // 生成随机头像
    user.avatar = new AvatarGenerator().generateRandomAvatar();
    // 过滤掉不需要的字段
    // delete user.confirmPassword;
    // 此步骤是为了让返回结果的password字段为空
    const plainUser = plainToInstance(UserEntity, { ...user }, { ignoreDecorators: true });
    const result = await getManager().transaction((transcationalEntityManager) => {
      return transcationalEntityManager.save<UserEntity>(plainUser);
    });
    return ResultData.ok(instanceToPlain(result));
  }
  /**
   * 查找用户
   * @param id 用户id
   * @returns
   */
  async findOneById(id: string) {
    const user = await this.userRepo.findOne(id);
    if (!user) {
      return null;
    }
    delete user.password;
    return user;
  }
  /**
   * 验证token
   * @param token
   * @returns
   */
  verifyToken(token: string): string {
    try {
      const id = this.jwt.verify(token.replace('Bearer ', ''));
      return id;
    } catch (err) {
      return null;
    }
  }

  /**
   * 刷新token
   * @param id 用户id
   * @returns
   */
  async updateToken(id: string): Promise<ResultData> {
    const data = await this.getToken({ id });
    return ResultData.ok(data);
  }
}
